/*
 * ESPRSSIF MIT License
 *
 * Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
 *
 * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP32 only, in which case,
 * it is free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_log.h"
#include "infra_compat.h"
#include "linkkit_solo.h"
#include "conn_mgr.h"

#include "driver/gpio.h"
#include "freertos/portmacro.h"
#include "dm_wrapper.h"
#include "conn_mgr.h"
#define LED3    12
#define LED2    13
#define CH3    16
#define CH2    5
#define CH1    4
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<LED3) | (1ULL<<LED2) | (1ULL<<CH3) | (1ULL<<CH2) | (1ULL<<CH1))
#define AWSS_KV_RST "awss.rst"

static const char *TAG = "app main";

static bool linkkit_started = false;
static bool Wifi_Flag = 0; 
static TaskHandle_t Work_STAUE_Task_Handle = NULL;

void GPIO_Init(void);
static void KEY_TASK(void);
static void Work_STAUE_Task(void *pvParameters);

static esp_err_t wifi_event_handle(void *ctx, system_event_t *event)
{
    switch (event->event_id)
    {
    case SYSTEM_EVENT_STA_GOT_IP:
        if (linkkit_started == false)
        {
            wifi_config_t wifi_config = {0};
            if (conn_mgr_get_wifi_config(&wifi_config) == ESP_OK &&
                strcmp((char *)(wifi_config.sta.ssid), HOTSPOT_AP) &&
                strcmp((char *)(wifi_config.sta.ssid), ROUTER_AP))
            {
                xTaskCreate((void (*)(void *))linkkit_main, "lightbulb", 10240, NULL, 5, NULL);
                linkkit_started = true;
            }
        }
        Wifi_Flag = 1 ; 
        break;

    default:
        break;
    }

    return ESP_OK;
}

static void linkkit_event_monitor(int event)
{
    switch (event)
    {
    case IOTX_AWSS_START: // AWSS start without enbale, just supports device discover
        // operate led to indicate user
        ESP_LOGI(TAG, "IOTX_AWSS_START");
        break;

    case IOTX_AWSS_ENABLE: // AWSS enable, AWSS doesn't parse awss packet until AWSS is enabled.
        ESP_LOGI(TAG, "IOTX_AWSS_ENABLE");
        // operate led to indicate user
        break;

    case IOTX_AWSS_LOCK_CHAN: // AWSS lock channel(Got AWSS sync packet)
        ESP_LOGI(TAG, "IOTX_AWSS_LOCK_CHAN");
        // operate led to indicate user
        break;

    case IOTX_AWSS_PASSWD_ERR: // AWSS decrypt passwd error
        ESP_LOGE(TAG, "IOTX_AWSS_PASSWD_ERR");
        // operate led to indicate user
        break;

    case IOTX_AWSS_GOT_SSID_PASSWD:
        ESP_LOGI(TAG, "IOTX_AWSS_GOT_SSID_PASSWD");
        // operate led to indicate user
        break;

    case IOTX_AWSS_CONNECT_ADHA: // AWSS try to connnect adha (device
        // discover, router solution)
        ESP_LOGI(TAG, "IOTX_AWSS_CONNECT_ADHA");
        // operate led to indicate user
        break;

    case IOTX_AWSS_CONNECT_ADHA_FAIL: // AWSS fails to connect adha
        ESP_LOGE(TAG, "IOTX_AWSS_CONNECT_ADHA_FAIL");
        // operate led to indicate user
        break;

    case IOTX_AWSS_CONNECT_AHA: // AWSS try to connect aha (AP solution)
        ESP_LOGI(TAG, "IOTX_AWSS_CONNECT_AHA");
        // operate led to indicate user
        break;

    case IOTX_AWSS_CONNECT_AHA_FAIL: // AWSS fails to connect aha
        ESP_LOGE(TAG, "IOTX_AWSS_CONNECT_AHA_FAIL");
        // operate led to indicate user
        break;

    case IOTX_AWSS_SETUP_NOTIFY: // AWSS sends out device setup information
        // (AP and router solution)
        ESP_LOGI(TAG, "IOTX_AWSS_SETUP_NOTIFY");
        // operate led to indicate user
        break;

    case IOTX_AWSS_CONNECT_ROUTER: // AWSS try to connect destination router
        ESP_LOGI(TAG, "IOTX_AWSS_CONNECT_ROUTER");
        // operate led to indicate user
        break;

    case IOTX_AWSS_CONNECT_ROUTER_FAIL: // AWSS fails to connect destination
        // router.
        ESP_LOGE(TAG, "IOTX_AWSS_CONNECT_ROUTER_FAIL");
        // operate led to indicate user
        break;

    case IOTX_AWSS_GOT_IP: // AWSS connects destination successfully and got
        // ip address
        ESP_LOGI(TAG, "IOTX_AWSS_GOT_IP");
        // operate led to indicate user
        break;

    case IOTX_AWSS_SUC_NOTIFY: // AWSS sends out success notify (AWSS
        // sucess)
        ESP_LOGI(TAG, "IOTX_AWSS_SUC_NOTIFY");
        // operate led to indicate user
        break;

    case IOTX_AWSS_BIND_NOTIFY: // AWSS sends out bind notify information to
        // support bind between user and device
        ESP_LOGI(TAG, "IOTX_AWSS_BIND_NOTIFY");
        // operate led to indicate user
        break;

    case IOTX_AWSS_ENABLE_TIMEOUT: // AWSS enable timeout
        // user needs to enable awss again to support get ssid & passwd of router
        ESP_LOGW(TAG, "IOTX_AWSS_ENALBE_TIMEOUT");
        // operate led to indicate user
        break;

    case IOTX_CONN_CLOUD: // Device try to connect cloud
        ESP_LOGI(TAG, "IOTX_CONN_CLOUD");
        // operate led to indicate user
        break;

    case IOTX_CONN_CLOUD_FAIL: // Device fails to connect cloud, refer to
        // net_sockets.h for error code
        ESP_LOGE(TAG, "IOTX_CONN_CLOUD_FAIL");
        // operate led to indicate user
        break;

    case IOTX_CONN_CLOUD_SUC: // Device connects cloud successfully
        ESP_LOGI(TAG, "IOTX_CONN_CLOUD_SUC");
        
        // operate led to indicate user
        break;

    case IOTX_RESET: // Linkkit reset success (just got reset response from
        // cloud without any other operation)
        ESP_LOGI(TAG, "IOTX_RESET");
        // operate led to indicate user
        break;

    default:
        break;
    }
}

static void start_conn_mgr()
{
    iotx_event_regist_cb(linkkit_event_monitor);            // 注册awss callback函数
    conn_mgr_start();

    vTaskDelete(NULL);
}


void app_main()
{
    GPIO_Init();

    //创建一个清除配网按键任务
    xTaskCreate((void (*)(void *))KEY_TASK, "KEY_TASK", 3072, NULL, 5, NULL);    

    //创建一个LED状态灯任务
    xTaskCreate(&Work_STAUE_Task, "Work_STAUE_Task", 1024*2, NULL, 4, Work_STAUE_Task_Handle); 

    conn_mgr_init();    //初始化WIFI的一些 配置
    conn_mgr_register_wifi_event(wifi_event_handle);    //注册wifi事件处理回调程序    

    IOT_SetLogLevel(IOT_LOG_INFO);      //设置打印日志的级别

    //创建一个AWSS连接任务
    xTaskCreate((void (*)(void *))start_conn_mgr, "conn_mgr", 3072, NULL, 5, NULL);

}

//初始化GPIO口
void GPIO_Init(void)    
{
    gpio_config_t io_conf;
    //禁止中断
    io_conf.intr_type = GPIO_INTR_DISABLE;
    //设置为输入模式
    io_conf.mode = GPIO_MODE_INPUT;
    //bit mask of the pins that you want to set,e.g.GPIO14
    io_conf.pin_bit_mask = ( 1ULL << GPIO_NUM_14);
    //disable pull-down mode
    io_conf.pull_down_en = 0;
    //disable pull-up mode
    io_conf.pull_up_en = 1;
    //configure GPIO with the given settings
    gpio_config(&io_conf);

    //disable interrupt
    io_conf.intr_type = GPIO_INTR_DISABLE;
    //set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;
    //bit mask of the pins that you want to set,e.g.GPIO12/13
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    //disable pull-down mode
    io_conf.pull_down_en = 0;
    //disable pull-up mode
    io_conf.pull_up_en = 0;
    //configure GPIO with the given settings
    gpio_config(&io_conf);

    gpio_set_level(LED3,1);
    gpio_set_level(LED2,1);
    gpio_set_level(CH3,0);
    gpio_set_level(CH2,0);
    gpio_set_level(CH1,0);
}

//配置一个清除配网信息按键
static void KEY_TASK(void)
{
    bool Perss_Flag = 0;
    char rst = 0x01;
    esp_err_t ret;

    while(1){
        if ( Perss_Flag == 0 )      //判断按键是否处于解锁状态
        {
            if ( gpio_get_level(GPIO_NUM_14) == 0 )
            {
                vTaskDelay( 10 / portTICK_PERIOD_MS );
                if ( gpio_get_level(GPIO_NUM_14) == 0 )
                {
                    Perss_Flag = 1 ;    //锁定按键
                    /*  since we cannot report reset status to cloud in this stage, just set the reset flag.
                        when connects to cloud, awss will do the reset report. */
                    ret = HAL_Kv_Set(AWSS_KV_RST, &rst, sizeof(rst), 0);
                    //清除存储的路由器SSID和密码
                    ret = conn_mgr_reset_wifi_config();
                    if ( ret == ESP_OK ){
                        printf("The WIFI SSID config is Earse\r\n");
                    }                          
                }
            }
        }
        if ( gpio_get_level(GPIO_NUM_14) == 1 )     //判断按键是否抬起，解除按键锁定
        {
            vTaskDelay( 10 / portTICK_PERIOD_MS );
            if ( gpio_get_level(GPIO_NUM_14) == 1 )
            {
                Perss_Flag = 0 ;
            }
        }
        vTaskDelay(10 / portTICK_PERIOD_MS);
    }
}


static void Work_STAUE_Task(void *pvParameters)
{

    while(1)
    {
        if( Wifi_Flag == 1 )                //判断WIFI是否连接
        {
            gpio_set_level(LED2, 0);
            vTaskDelay(10 / portTICK_PERIOD_MS);
            // printf("Wifi is conect\r\n");
        }
        else
        {
            // printf("Wifi is disconect\r\n");
            gpio_set_level(LED2, 1);
            vTaskDelay(500 / portTICK_PERIOD_MS);
            gpio_set_level(LED2, 0);
            vTaskDelay(500 / portTICK_PERIOD_MS);
        }
    }
}